/* eslint-disable @typescript-eslint/ban-ts-comment */
import { FitAddon } from '@xterm/addon-fit';
import { ISearchOptions, SearchAddon } from '@xterm/addon-search';
import { ITerminalOptions, ITheme, Terminal } from '@xterm/xterm';

import { IXTerm } from '../common/xterm';

import styles from './component/terminal.module.less';

import type { CanvasAddon as CanvasAddonType } from '@xterm/addon-canvas';
import type { WebglAddon as WebglAddonType } from '@xterm/addon-webgl';
import { Disposable } from '../../utils';

export interface XTermOptions {
  cwd?: string;
}

export class XTerm extends Disposable implements IXTerm {
  container: HTMLDivElement; // 终端容器 DOM

  raw: Terminal; // xterm.js 实例

  /** addons */
  private _fitAddon: FitAddon; // 自适应尺寸插件
  private _searchAddon: SearchAddon; // 搜索插件
  private _webglAddon?: WebglAddonType; // WebGL 渲染插件
  private _canvasAddon?: CanvasAddonType; // Canvas 渲染插件
  /** end */

  constructor(public options: XTermOptions) {
    super();
    this.container = document.createElement('div');
    this.container.className = styles.terminalInstance;

    // 创建 xterm 实例
    this.raw = new Terminal({
      allowProposedApi: true,
    });
    this._prepareAddons();
    this.raw.onSelectionChange(this.onSelectionChange.bind(this));
  }

  // 启用 Canvas 渲染器
  protected async enableCanvasRenderer() {
    try {
      if (!this._canvasAddon) {
        // @ts-ignore
        this._canvasAddon = new (
          await import('@xterm/addon-canvas')
        ).CanvasAddon();
      }

      this.addDispose(this._canvasAddon);
      this.raw.loadAddon(this._canvasAddon);

      if (this._webglAddon) {
        this._webglAddon.dispose();
        this._webglAddon = undefined;
      }
    } catch (err) {
      // ignore
    }
  }

  // 启用 WebGL 渲染器
  protected async enableWebglRenderer() {
    try {
      if (!this._webglAddon) {
        // @ts-ignore
        this._webglAddon = new (
          await import('@xterm/addon-webgl')
        ).WebglAddon();
      }

      this.addDispose(this._webglAddon);
      this.addDispose(
        this._webglAddon.onContextLoss(() => {
          // @ts-ignore
          this.raw.options.rendererType = 'dom';
        })
      );
      this.raw.loadAddon(this._webglAddon);
      if (this._canvasAddon) {
        this._canvasAddon.dispose();
        this._canvasAddon = undefined;
      }
    } catch (err) {
      await this.enableCanvasRenderer();
    }
  }

  // 初始化插件
  private async _prepareAddons() {
    this._searchAddon = new SearchAddon();
    this._fitAddon = new FitAddon();

    this.addDispose([this._searchAddon, this._fitAddon]);

    this.raw.loadAddon(this._searchAddon);
    this.raw.loadAddon(this._fitAddon);
  }

  // 更新终端主题
  updateTheme(theme: ITheme | undefined) {
    if (theme) {
      this.raw.options.theme = theme;
    }
  }

  // 获取查找高亮相关颜色
  // 查找下一个匹配项
  findNext(text: string, searchOptions: ISearchOptions = {}) {
    const options: ISearchOptions = {
      ...searchOptions,
    };
    return this._searchAddon.findNext(text, options);
  }

  // 查找上一个匹配项
  findPrevious(text: string, searchOptions: ISearchOptions = {}) {
    const options: ISearchOptions = {
      ...searchOptions,
    };
    return this._searchAddon.findPrevious(text, options);
  }

  // 搜索结果变更事件
  get onSearchResultsChange() {
    return this._searchAddon.onDidChangeResults;
  }

  // 关闭搜索并清除高亮
  closeSearch() {
    this._searchAddon.clearDecorations();
  }

  // 挂载终端到容器
  open() {
    this.raw.open(this.container);
    // // 根据配置选择渲染类型
    // const renderType = this.preferenceService.get<RenderType>(CodeTerminalSettingId.XtermRenderType, RenderType.WebGL);
    // if (renderType === RenderType.WebGL) {
    this.enableWebglRenderer();
    // } else if (renderType === RenderType.Canvas) {
    //   this.enableCanvasRenderer();
    // }
    // 不设置 enableWebGL/Canvas render 的话，默认就会 fallback 到 DOM Render
  }

  // 适应容器尺寸
  fit() {
    this._fitAddon.fit();
  }

  // 选区变更时自动复制
  async onSelectionChange() {}

  // 复制选中内容到剪贴板
  async copySelection() {
    if (this.raw.hasSelection()) {
      // await this.clipboardService.writeText(this.raw.getSelection());
    } else {
      // this.messageService.warning('The terminal has no selection to copy');
    }
  }

  // 销毁终端
  dispose() {
    this.raw.dispose();
    this.container.remove();
  }
}
